package utils

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"github.com/go-playground/validator/v10"
	"golang.org/x/crypto/bcrypt"
	"log"
	"math/rand"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

var (
	SUCCESS      = 200
	ERROR        = 300
	STATUSNORMAL = 1
	NOT_DELETE   = 0
	MYSECRET     = []byte("KsLYcwVNh0W1mmxgy43TjsQtXVIi")
	Page         = 1
	Size         = 10
	Err          error

	TotalRows  int64
	TotalPages int64
)

type RList[T any] struct {
	List       []T   `json:"list"`
	TotalPages int64 `json:"total_pages"`
	Page       int   `json:"page"`
	Size       int   `json:"size"`
	TotalRows  int   `json:"totalRows"`
}

type Response struct {
	Code int    `json:"code"`
	Data any    `json:"data"`
	Msg  string `json:"msg"`
}

type ResDataToken struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Token string `json:"token"`
}

// UploadRes 文件上传用
type UploadRes struct {
	Rid      uint   `json:"r_id"`
	FileName string `json:"file_name"`
	Code     int    `json:"code"`
	Msg      string `json:"msg"`
}

type PubStatus struct {
	Status int
	Msg    string
}

// GetStructValByColumnUint 提取结构体指定字段的值 存进新的切片中 用于字id
// 接受两个参数：data 是传入的结构体切片，fieldName 是要提取的字段名称。
// 该函数内部使用了反射来获取传入的切片值的类型，并使用 reflect.Indirect() 来获取每个结构体的字段值
func GetStructValByColumnUint(data interface{}, column string) []int {
	rv := reflect.ValueOf(data)
	result := []int{}

	if rv.Kind() == reflect.Slice {
		for i := 0; i < rv.Len(); i++ {

			elem := rv.Index(i)

			if elem.Kind() == reflect.Struct {

				idField := elem.FieldByName(column)

				if idField.IsValid() && idField.Kind() == reflect.Uint {
					result = append(result, int(idField.Uint()))

				}
			}
		}
	}
	return result
}

// Result 基础返回方法
func Result(code int, data any, msg string, c *gin.Context) {
	c.JSON(http.StatusOK, Response{
		Code: code,
		Data: data,
		Msg:  msg,
	})
}

// Success 成功的
func Success(data any, msg string, c *gin.Context) {
	Result(SUCCESS, data, msg, c)
}

// Fail 异常的
func Fail(data any, msg string, c *gin.Context) {
	Result(ERROR, data, msg, c)
}

// UnixToTime 时间戳 转 时间
func UnixToTime(timestamp int) string {
	t := time.Unix(int64(timestamp), 0)
	return t.Format("2006-01-02 15:04:05")
}

// TimeToUnix 时间 转 时间戳
func TimeToUnix(str string) int64 {
	template := "2006-01-02 15:04:05"
	t, err := time.ParseInLocation(template, str, time.Local)
	if err != nil {
		return 0
	}
	return t.Unix()
}

// DateToUnix 日期 转 时间戳
func DateToUnix(str string) int64 {
	template := "2006-01-02"
	t, err := time.ParseInLocation(template, str, time.Local)
	if err != nil {
		return 0
	}
	return t.Unix()
}

// GetUnix 获取时间戳
func GetUnix() int64 {
	return time.Now().Unix()
}

// GetDateTime 获取时间日期
func GetDateTime() string {
	tmp := "2006-01-02 15:04:05"
	return time.Now().Format(tmp)
}

// GetDate 获取年月日
func GetDate() string {
	tmp := "20060102"
	return time.Now().Format(tmp)
}

func MD5(src []byte) string {
	m := md5.New()
	m.Write(src)
	return hex.EncodeToString(m.Sum(nil))
}

// EncryptEmail email截取加密
func EncryptEmail(email string) string {
	eList := strings.Split(email, "@")
	if len(eList) != 2 {
		return ""
	}
	// 123@qq.com
	// 1dsfw32r2@163.com
	// 返回 1****@qqq.com 1****@163.com
	return eList[0][:1] + "****@" + eList[1]
}

// EncryptPhone 手机号截取加密
func EncryptPhone(phone string) string {
	if len(phone) != 11 {
		return ""
	}
	// 18666666666
	return phone[:3] + "****" + phone[7:]
}

// HashPassword 加密密码
func HashPassword(pwd []byte) string {
	hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
	if err != nil {
		log.Println(err)
	}
	return string(hash)
}

// CheckPassword 验证密码
func CheckPassword(hashedPwd string, plainPwd []byte) bool {
	byteHash := []byte(hashedPwd)

	err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)
	if err != nil {
		log.Println(err)
		return false
	}
	return true
}

// GetValidMsg 解析验证请求数据的错误信息
func GetValidMsg(err error, obj any) string {
	getObj := reflect.TypeOf(obj)
	// 通过使用 errors.As 函数将错误对象断言为 validator.ValidationErrors 类型的变量，并检查断言是否成功
	var errs validator.ValidationErrors
	if errors.As(err, &errs) {
		// 断言成功
		for _, e := range errs {
			if f, exits := getObj.Elem().FieldByName(e.Field()); exits {
				msg := f.Tag.Get("msg")
				return msg
			}
		}
	}
	return err.Error()
}

func GenerateRandomStringMath(n int) string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
	result := make([]byte, n)
	for i := range result {
		result[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(result)
}
